//
//  QNTaskScheduler.m
//  Pods
//
//  Created by qiudongquan on 2019/8/21.
//

#import "QNTaskScheduler.h"
#import "QNCategory.h"
#import "NSError+QNCentral.h"

@interface QNTaskScheduler ()

@property(nonatomic, strong) id responseData;

@property(nonatomic, strong) QNTaskExecutCallback executCallback;

@property(nonatomic, strong) QNTaskExecutCheckCallback executCheckCallback;

@property(nonatomic, strong) QNTaskCompleteCallback completeCallback;

@property(nonatomic, strong) NSError *err;

@property(nonatomic, strong) NSTimer *timer;

@property(nonatomic, assign) NSTimeInterval timeInterval;

@property(nonatomic, strong) NSThread *currentThread;

@end

@implementation QNTaskScheduler
@synthesize executing = _executing;
@synthesize finished = _finished;

- (BOOL)isConcurrent {
    return YES;
}

- (BOOL)isExecuting {
    return _executing;
}

- (BOOL)isFinished {
    return _finished;
}

- (void)start {
    if (self.isCancelled) {
        [self willChangeValueForKey:@"isFinished"];
        _finished = YES;
        [self didChangeValueForKey:@"isFinished"];
        return;
    }
    
    [self willChangeValueForKey:@"isExecuting"];
    [NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil];
    _executing = YES;
    [self didChangeValueForKey:@"isExecuting"];
}

- (void)main {
    @autoreleasepool {
        __weak __typeof(self)weakSelf = self;
        self.currentThread = [NSThread currentThread];
        BOOL isMainThread = [NSThread isMainThread];
        if (self.executCheckCallback) {
            if (isMainThread) {
                self.err = self.executCheckCallback(weakSelf);
            }else {
                dispatch_sync(dispatch_get_main_queue(), ^{
                    self.err = self.executCheckCallback(weakSelf);
                });
            }
        }
        
        if (self.err) {
            [self completeTaskWithResponseData:weakSelf.responseData err:weakSelf.err];
            return;
        }
        
        if (self.executCallback) {
            if ([NSThread isMainThread]) {
                self.executCallback(weakSelf);
            }else {
                dispatch_sync(dispatch_get_main_queue(), ^{
                    self.executCallback(weakSelf);
                });
            }
        }
        
        if (self.timeInterval > 0 ) {
            [self startTimer];
        }
        
        [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
        while (_finished == NO) {
            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
        }
    }
}

- (void)startTimer {
    [self cancelTimer];
    __weak __typeof(self)weakSelf = self;
    self.timer = [NSTimer qnCentralScheduledTimerWithTimeInterval:self.timeInterval block:^(NSTimer * _Nonnull timer) {
        [weakSelf cancelTimer];
        [weakSelf completeTaskWithResponseData:weakSelf.responseData err:[NSError errorWithCode:QNCentralResponseTimeover]];
    } repeats:NO];
}

- (void)cancelTimer {
    [self.timer invalidate];
    self.timer = nil;
}

- (instancetype)initWithData:(id)data timeInterval:(NSTimeInterval)timeInterval executCheckCallback:(QNTaskExecutCheckCallback _Nullable)executCheckCallback executCallback:(QNTaskExecutCallback _Nullable)executCallback completeCallback:(QNTaskCompleteCallback _Nullable)completeCallback {
    if (self = [super init]) {
        _executing = NO;
        _finished = NO;
        if (data != nil) {
            [self setValue:data forKeyPath:@"data"];
        }
        self.timeInterval = timeInterval;
        self.executCallback = executCallback;
        self.executCheckCallback = executCheckCallback;
        self.completeCallback = completeCallback;
    }
    return self;
}


- (void)completeTask {
    if (_finished) return;
    @autoreleasepool {
        [self cancelResponseTimer];
        __weak __typeof(self)weakSelf = self;
        if (self.completeCallback) {
            if ([NSThread isMainThread]) {
                self.completeCallback(weakSelf,weakSelf.responseData,weakSelf.err);
            }else {
                dispatch_sync(dispatch_get_main_queue(), ^{
                    self.completeCallback(weakSelf,weakSelf.responseData,weakSelf.err);
                });
            }
        }
        
        [self willChangeValueForKey:@"isExecuting"];
        _executing = NO;
        [self didChangeValueForKey:@"isExecuting"];
        
        [self willChangeValueForKey:@"isFinished"];
        _finished  = YES;
        [self didChangeValueForKey:@"isFinished"];
    }
}

#pragma mark - 外部方法
- (void)cancelResponseTimer {
    if (self.currentThread == [NSThread currentThread]) {
        [self cancelTimer];
    }else {
        [self performSelector:@selector(cancelTimer) onThread:self.currentThread withObject:nil waitUntilDone:NO];
    }
}

- (void)extendTaskSchedulerTimerWithInterval:(NSTimeInterval)timeInterval {
    if (_finished) return;
    self.timeInterval = timeInterval;
    if (self.currentThread == [NSThread currentThread]) {
        [self startTimer];
    }else {
        [self performSelector:@selector(startTimer) onThread:self.currentThread withObject:nil waitUntilDone:NO];
    }
}

- (void)updateResponseData:(id)data {
    self.responseData = data;
}

- (void)completeTaskWithResponseData:(id)data err:(NSError *)err {
    self.responseData = data;
    self.err = err;
    if ([NSThread currentThread] == self.currentThread) {
        [self completeTask];
    }else {
        [self performSelector:@selector(completeTask) onThread:self.currentThread withObject:nil waitUntilDone:NO];
    }
}

@end
